using System;
using System.Collections.Generic;
using System.Text;

namespace Tortuga.Core.TortoiseCommands {

	#region Base Commands
	public class TortoiseCommand {
		public delegate string BuildOptionDelegate();
		private string _commandName = "";
		private List<string> _paths = new List<string>();
		private bool _noTempFile = true;
		private CloseOnEnd _closeOnEnd = CloseOnEnd.DontClose;
		private string _pathDelimiter = "*";
		private List<BuildOptionDelegate> _optionsToBuild = new List<BuildOptionDelegate>();
		
		#region Properties
		/// <summary>
		/// Gets or sets a collection of methods that need to be run in order to build this command.
		/// </summary>
		public List<BuildOptionDelegate> OptionsToBuild {
			get { return _optionsToBuild; }
			set { _optionsToBuild = value; }
		}
		
		/// <summary>
		/// Gets or sets the value that separates multiple paths.
		/// </summary>
		public string PathDelimiter {
			get { return _pathDelimiter; }
			set { _pathDelimiter = value; }
		}

		/// <summary>
		/// Gets or sets the command to be built.  e.g., "commit"
		/// </summary>
		public virtual string CommandName {
			get { return _commandName; }
			private set { _commandName = value; }
		}

		/// <summary>
		/// Gets or sets the paths to be included in this command.
		/// </summary>
		public virtual List<string> Path {
			get { return _paths; }
			set { _paths = value; }
		}

		/// <summary>
		/// Gets or sets whether no temp file(s) should be created.  This should always be true!
		/// </summary>
		public virtual bool NoTempFile {
			get { return _noTempFile; }
			set { _noTempFile = value; }
		}

		/// <summary>
		/// Gets or sets the closing action to be performed to the UI dialog when the command has completed executing.
		/// </summary>
		public virtual CloseOnEnd CloseOnEndValue {
			get { return _closeOnEnd; }
			set { _closeOnEnd = value; }
		}

		#endregion 

		#region Constructors
		public TortoiseCommand() { }

		/// <summary>
		/// Instantiates a TortoiseCommand with the specified name.
		/// </summary>
		/// <param name="commandName"></param>
		public TortoiseCommand(string commandName):this() {
			this.CommandName = commandName;
			this._optionsToBuild = new List<BuildOptionDelegate>(new BuildOptionDelegate[] {
			      BuildPathOption, BuildNoTempFile, BuildCloseOnEnd
			});
		}
		#endregion

		#region Build Finalization
		/// <summary>
		/// Builds and returns the command in its current state.
		/// </summary>
		/// <returns></returns>
		protected virtual string Build() {
			if(ValidateOptions()) {
				List<string> commands = new List<string>();
				StringBuilder builder = new StringBuilder();
				builder.AppendFormat("/command:{0} ", CommandName);
				
				foreach(BuildOptionDelegate option in OptionsToBuild) {
					string built = option(); 
					// We don't want any empty options in here to prevent unneeded whitespace. 
					// It doesn't really matter to TortoiseProc but the whitespace mucks up my testing.
					if(built.Length > 0) commands.Add(built);
				}
	
				builder.Append(String.Join(" ", commands.ToArray()));
				return builder.ToString();
			} else {
				throw new TortoiseException("There is a problem with the command you have built.  Please ensure all appropriate parameters have been defined.");
			}
		}

		/// <summary>
		/// Validates that everything needed is present in this object.  Override this if you need to check certain options before the build occurs.
		/// </summary>
		/// <returns>true</returns>
		protected virtual bool ValidateOptions() {
			return true; 
		}
		
		/// <summary>
		/// Builds and returns the command in its current state.
		/// </summary>
		/// <returns></returns>
		public override string ToString() {
			return Build();
		}
		#endregion

		#region Generic Option Building

		/// <summary>
		/// Builds an option containing a string value -- surrounded with quotation marks -- from the passed data.
		/// </summary>
		/// <param name="name">The name of the option. (e.g., "path")</param>
		/// <param name="value">The value to be attributed to this option.  (e.g., "d:\temp\yourfile.txt")</param>
		/// <returns>/name:"value"</returns>
		public static string BuildOption(string name, string value) {
			if (!String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(value)) {
				return String.Format("/{0}:\"{1}\"", name.Trim(), value.Trim()).Trim();
			}
			return "";
		}

		/// <summary>
		/// Builds an option specifying a boolean variable.  
		/// </summary>
		/// <param name="name">The name of the option.  (e.g., "closeonend")</param>
		/// <param name="value">The value to be attributed to this option.  If false, nothing will be generated.</param>
		/// <returns>/name</returns>
		public static string BuildOption(string name, bool value) {
			return (String.IsNullOrEmpty(name) || !value) ?
				"" : String.Format("/{0}", name);
		}

		/// <summary>
		/// Builds an option containing an integer value.  If value is null nothing is generated.
		/// </summary>
		/// <param name="name">The name of the option.  (e.g. "revstart")</param>
		/// <param name="value">The value to be attributed to this option.  (e.g., 40)</param>
		/// <returns>/name:value</returns>
		public static string BuildOption(string name, int? value) {
			return (String.IsNullOrEmpty(name) || !value.HasValue) ?
				"" : String.Format("/{0}:{1}", name, value.Value.ToString());
		}
		#endregion

		#region Building No Temp File
		/// <summary>
		/// Builds a No Temp File option.
		/// </summary>
		/// <param name="noTempFile">Whether no temp file should be created.  (Why you would set this to false, I don't know.)</param>
		/// <returns>/notempfile</returns>
		public static string BuildNoTempFile(bool noTempFile) {
			return BuildOption("notempfile", noTempFile);
		}

		/// <summary>
		/// Builds a No Temp File option using the NoTempFile value.
		/// </summary>
		/// <returns>/notempfile</returns>
		public virtual string BuildNoTempFile() {
			return BuildNoTempFile(NoTempFile);
		}
		#endregion

		#region Building Close on End

		/// <summary>
		/// Builds an option containing the passed CloseOnEnd value.
		/// </summary>
		/// <param name="closeOnEnd">The action to be performed on end.</param>
		/// <returns>/closeonend:Convert.ToInt32(closeOnEnd)</returns>
		public static string BuildCloseOnEnd(CloseOnEnd closeOnEnd) {
			return BuildOption("closeonend", Convert.ToInt32(closeOnEnd));
		}

		/// <summary>
		/// Builds an option containing the CloseOnEnd value.
		/// </summary>
		/// <returns>/closeonend:Convert.ToInt32(CloseOnEnd)</returns>
		public virtual string BuildCloseOnEnd() {
			return BuildCloseOnEnd(CloseOnEndValue);
		}
		#endregion
		
		#region Building Paths

		/// <summary>
		/// Builds the path option if any paths have been added.
		/// </summary>
		/// <returns></returns>
		public virtual string BuildPathOption() {
			return BuildPathOption(PathDelimiter, Path.ToArray());
		}

		/// <summary>
		/// Builds the path option including the passed paths.
		/// </summary>
		/// <param name="delimiter">The string that separates multiple paths.</param>
		/// <param name="paths">The paths to include in this command.</param>
		/// <returns></returns>
		public static string BuildPathOption(string delimiter, params string[] paths) {
			return BuildOption("path", String.Join(delimiter, paths)).Trim();
		}
		#endregion

		#region Building Url
		/// <summary>
		/// Builds a Url option.  
		/// </summary>
		/// <param name="url">The Url</param>
		/// <returns>/url:"url"</returns>
		public static string BuildUrlOption(string url) {
			return BuildOption("url", url);
		}


		/// <summary>
		/// Builds a rev option
		/// </summary>
		/// <param name="rev">The revision value.</param>
		/// <returns>/rev:7 (where rev.Value = 7)</returns>
		public static string BuildRevOption(Revision rev) {
			return BuildRevisionOption(rev, "rev");
		}

		/// <summary>
		/// Builds a option with the desired name based on a revision
		/// </summary>
		/// <param name="rev">The revision value.</param>
		/// <returns>/name:7 (where rev.Value = 7)</returns>
		public static string BuildRevisionOption(Revision rev, string name) {
			return (!rev.Value.HasValue || rev.Value.Value == Revision.Head.Value.Value) ?
				"" : BuildOption(name, rev.Value.Value);
		}

		#endregion

	}

	/// <summary>
	/// Creates a TortoiseCommand that accepts only one path rather than a list.
	/// </summary>
	public class SinglePathTortoiseCommand : TortoiseCommand {
		private string _path = "";

		/// <summary>
		/// Instantiates a TortoiseCommand with the specified name and path.
		/// </summary>
		/// <param name="commandName"></param>
		/// <param name="path"></param>
		protected SinglePathTortoiseCommand(string commandName, string path)
			: base(commandName) {
			this.Path = path;
		}

		/// <summary>
		/// Builds a path option containing the value in Path.
		/// </summary>
		/// <returns>/path:"Path"</returns>
		public override string BuildPathOption() {
			return BuildOption("path", this.Path);
		}

		/// <summary>
		/// Gets or sets the single path for this command.
		/// </summary>
		public new string Path {
			get { return _path; }
			set { _path = value; }
		}
	}

	/// <summary>
	/// Creates a TortoiseCommand that accepts a Start and End Revision.
	/// </summary>
	public class RevisionRangeTortoiseCommand : TortoiseCommand {
		private Revision _revisionStart = new Revision(null);
		private Revision _revisionEnd = new Revision(null);

		/// <summary>
		/// Instantiates a TortoiseCommand that accepts a Start and End Revision.
		/// </summary>
		/// <param name="commandName"></param>
		public RevisionRangeTortoiseCommand(string commandName) : base(commandName) {
			this.OptionsToBuild.Add(this.BuildRevisionStartOption);
			this.OptionsToBuild.Add(this.BuildRevisionEndOption);
		}

		/// <summary>
		/// Gets or sets the start revision.
		/// </summary>
		public virtual Revision RevisionStart {
			get { return _revisionStart; }
			set { _revisionStart = value; }
		}

		/// <summary>
		/// Gets or sets the end revision.
		/// </summary>
		public virtual Revision RevisionEnd {
			get { return _revisionEnd; }
			set { _revisionEnd = value; }
		}

		#region Building Revisions
		/// <summary>
		/// Builds the /revstart option if RevisionStart is not null.
		/// </summary>
		/// <returns></returns>
		public virtual string BuildRevisionStartOption() {
			return BuildRevisionStartOption(RevisionStart);
		}

		/// <summary>
		/// Builds a /revstart option with the passed Revision.Value if not null.
		/// </summary>
		/// <param name="revision">The revision </param>
		/// <returns></returns>
		public static string BuildRevisionStartOption(Revision revision) {
			return BuildOption("revstart", revision.Value);
		}

		/// <summary>
		/// Builds the /revend option with the passed Revision.Value if not null.
		/// </summary>
		/// <returns></returns>
		public virtual string BuildRevisionEndOption() {
			return BuildRevisionEndOption(RevisionEnd);
		}

		/// <summary>
		/// Builds the /revend option with the passed Revision.Value if not null.
		/// </summary>
		/// <returns></returns>
		public static string BuildRevisionEndOption(Revision revision) {
			return BuildOption("revend", revision.Value);
		}
		#endregion
	}

	/// <summary>
	/// The possible values for a CloseOnEnd option.
	/// </summary>
	public enum CloseOnEnd {
		/// <summary>
		/// Don't close the dialog at all.  User must close.
		/// </summary>
		DontClose = 0,

		/// <summary>
		/// Close the dialog if there are no errors.
		/// </summary>
		IfNoErrors = 1,

		/// <summary>
		/// Close the dialog if there are no conflicts.
		/// </summary>
		IfNoConflicts = 2,

		/// <summary>
		/// Close the dialog if there are no merges.
		/// </summary>
		IfNoMerges = 3
	}
	#endregion

}